﻿Imports System.Runtime.InteropServices

Public Class ComboBox : Inherits TextBox

	Dim NoEvents As Boolean
	Public WithEvents Results As New Grid
	Dim WithEvents DropdownMenu As New ToolStripDropDown
	Dim Host As ToolStripControlHost

	Sub New()
		MyBase.New()
		OnCreateDropDown()
		ShowDropDownButton = True
		List = True
		DropdownMenu.AutoSize = True
		DropdownMenu.Items.Clear()
		DropdownMenu.Margin = New Padding(0)
		DropdownMenu.Padding = New Padding(0)
	End Sub

	Property DataWindow As Boolean

	Overridable Sub OnCreateDropDown()
		Results.ColumnHeadersVisible = False
		Results.AddColumns("kod(h),ad(;100%l)")
	End Sub

	Public Overrides Sub Reset()
		MyBase.Reset()
		If InitialSource > "" Then Source = InitialSource
		Value = [Default]
	End Sub

	Sub CloseMenu()
		DropdownMenu.Hide()
	End Sub

	Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
		If List Then
			Focus()
			OnDropDownButtonClicked(e)
			Return
		End If
		MyBase.OnMouseDown(e)
	End Sub

	Dim m_Value As Object = ""
	Public Overrides Property Value() As Object
		Get
			Return m_Value
		End Get
		Set(ByVal Value As Object)
			If IsDBNull(Value) Then Value = ""
			Select Case LCase(Trim(Value))
				Case "--first--" : If Results.RowCount > 0 Then Results.Rows(0).Selected = True : SelectItem() : Return
				Case "--last--" : If Results.RowCount > 0 Then Results.Rows(Results.RowCount - 1).Selected = True : SelectItem() : Return
			End Select
			For Each Row As DataGridViewRow In Results.Rows
				If Trim(IsNull(Row.Cells(0).Value, "")) = Trim(Value) Then
					Text = IsNull(Row.Cells(1).Value, "")
					Results.HighlightRow(Row.Index)
					Exit For
				End If
			Next
			Value = Trim(Value)
			m_Value = Trim(Value)
			If Value = "" Then Text = ""
		End Set
	End Property

	Protected Overrides Sub OnTextChanged(e As System.EventArgs)
		MyBase.OnTextChanged(e)
		m_Value = ""
		If Text <> "" Then
			Dim a = (From row As DataGridViewRow In Results.Rows Let v = LCase(Trim(IsNull(row.Cells(1).Value, ""))) Where v = LCase(Text) OrElse (Text.EndsWith("/") AndAlso Mid(v, 1, Len(Text)) = LCase(Text))).FirstOrDefault
			If Not a Is Nothing Then
				m_Value = a.row.Cells(0).Value
			End If
		End If
		If List AndAlso Trim(m_Value) = "" AndAlso Text.Length > 1 Then
			Dim a = (From row As DataGridViewRow In Results.Rows Where LCase(Trim(row.Cells(1).Value)(0)) = LCase(Text.Right(1)) Select row).FirstOrDefault
			If Not a Is Nothing Then
				Text = a.Cells(1).Value
				SelectAll()
			End If
		End If
		RaiseChangeEvent()
	End Sub

	Public InitialSource As String

	Dim m_Source As Recordset
	Property Source() As Object
		Get
			Return m_Source
		End Get
		Set(ByVal Value As Object)
			If IsNothing(Value) Then Exit Property
			NoEvents = True
			If TypeOf Value Is Recordset Then
				m_Source = Value
			Else
				If InStr(Value, "select") = 1 Then
					m_Source = Process.Remote.OpenSQL(Value, 3)
				ElseIf Mid(Value, 1, 2) = "p@" Then
					m_Source = Process.Remote.OpenSQL("select Deger, Ad from Parametre where Grup = :grup order by Sira, Ad", Mid(Value, 3))
				ElseIf Mid(Value, 1, 2) = "q@" Then
					m_Source = Process.Remote.OpenSQL(Mid(Value, 3))
				Else
					m_Source = New Recordset("kod(s),ad(s)")
					Dim l As New SortedList(Of Integer, Char)
					For Each c In ",;|:"
						Dim p = InStr(Value, c)
						If p > 0 Then l.Add(p, c)
					Next
					If l.Count = 0 Then l.Add(0, "\")
					If l.Count = 1 Then l.Add(1, "~")
					For Each Item In Split(Value, l.Values(1))
						If Item <> "" Then
							If Not Item.Contains(l.Values(0)) Then Item &= l.Values(0) & Item
							Dim Data = Split(Item, l.Values(0))
							m_Source.AddNew("kod,ad", Data(0), Data(1))
						End If
					Next
				End If
			End If
			Results.Rows.Clear()
			Results.RowTemplate.DefaultCellStyle.Padding = New Padding(3, 0, 3, 0)
			AutoCompleteMode = Windows.Forms.AutoCompleteMode.None
			AutoCompleteSource = Windows.Forms.AutoCompleteSource.CustomSource
			Dim Source As New AutoCompleteStringCollection
			For Each Row In m_Source
				Results.Rows.Add(m_Source(0), m_Source(1))
				Source.Add(IsNull(m_Source(1), ""))
			Next
			AutoCompleteCustomSource = Source
			AutoCompleteMode = Windows.Forms.AutoCompleteMode.Append
			NoEvents = False
		End Set
	End Property

	Property DroppedDown As Boolean
		Get
			Return DropdownMenu.Visible
		End Get
		Set(value As Boolean)
			If value Then
				DropdownMenu.Items.Clear()
				Host = New ToolStripControlHost(GetDropDownControl)
				DropdownMenu.Items.Add(Host)
				Host.Margin = New Padding(0)
				Host.Padding = New Padding(0)
				Cursor = Cursors.Default
				Host.Padding = New Padding(1)
				Host.Margin = New Padding(0)
				Host.BackColor = SystemColors.ButtonFace
				If DataWindow Then
					DropdownMenu.Show(Me, New Point(-2, Height + 2), ToolStripDropDownDirection.Default)
				Else
					DropdownMenu.Show(Me, New Point(-3, Height + 3), ToolStripDropDownDirection.Default)
				End If
				Host.Control.Focus()
				Host.Control.Capture = True
			Else
				DropdownMenu.Hide()
			End If
		End Set
	End Property

	Overridable Function GetDropDownControl() As Control
		Dim w = Width + 4
		If DataWindow Then w -= 2
		Results.MinimumSize = New Size(w, Math.Min(10, Results.Rows.Count) * Results.Rows(0).Height)
		Results.MaximumSize = Results.MinimumSize
		Return Results
	End Function

	Dim WithEvents TmrSelect As New Timer With {.Interval = 1000}

	Protected Overrides Sub OnKeyDown(e As System.Windows.Forms.KeyEventArgs)
		If List AndAlso (e.KeyCode = Keys.Delete OrElse e.KeyCode = Keys.Back) Then
			Value = ""
			e.Handled = True
		End If
		MyBase.OnKeyDown(e)
	End Sub

	Dim Buffer As String = ""
	Protected Overrides Sub OnKeyPress(e As System.Windows.Forms.KeyPressEventArgs)
		If List Then
			EvaluateKey(e.KeyChar)
			If Not DroppedDown Then DroppedDown = True
			e.Handled = True
		End If
		If e.KeyChar = Chr(13) Then Stop
		MyBase.OnKeyPress(e)
	End Sub

	Sub EvaluateKey(c As Char)
		TmrSelect.Enabled = True
		Buffer &= LCase(c)
		Dim CurrentRow = (From r As DataGridViewRow In Results.Rows Where r.Cells(1).Value = Text).FirstOrDefault
		Dim i = 0
		Dim NextItem = (From r As DataGridViewRow In Results.Rows Where r.Index >= i AndAlso Mid(r.Cells(1).Value, 1, Buffer.Length).ToLower = Buffer).FirstOrDefault
		If NextItem Is Nothing AndAlso Buffer.Length = 2 AndAlso Buffer(0) = Buffer(1) Then
			Buffer = Buffer.Right(1)
			i = If(CurrentRow Is Nothing, 0, CurrentRow.Index + 1)
			NextItem = (From r As DataGridViewRow In Results.Rows Where r.Index >= i AndAlso Mid(r.Cells(1).Value, 1, Buffer.Length).ToLower = Buffer).FirstOrDefault
		End If
		If NextItem Is Nothing Then
			Buffer = Buffer.Right(1)
			NextItem = (From r As DataGridViewRow In Results.Rows Where Mid(r.Cells(1).Value, 1, Buffer.Length).ToLower = Buffer).FirstOrDefault
		End If
		If Not NextItem Is Nothing Then
			Value = NextItem.Cells(0).Value
		End If
	End Sub

	Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
		If keyData = Keys.Alt + Keys.Down Then
			DroppedDown = True
			Return True
		ElseIf keyData = Keys.Down Then
			DroppedDown = True
			Return True
		ElseIf keyData = Keys.Up Then
			DroppedDown = True
			Return True
		End If
		Return MyBase.ProcessCmdKey(msg, keyData)
	End Function

	Private Sub Results_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles Results.KeyPress
		If List Then
			e.Handled = True
			If Asc(e.KeyChar) >= 32 Then EvaluateKey(e.KeyChar)
		End If
	End Sub

	Private Sub Results_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Results.MouseDown
		SelectItem()
		SelectAll()
	End Sub

	Dim _List As Boolean
	Property List As Boolean
		Get
			Return _List
		End Get
		Set(value As Boolean)
			_List = value
			ShowCursor = Not List
		End Set
	End Property

	Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs)
		MyBase.OnPaintBackground(e)
		If List AndAlso (Focused OrElse DroppedDown) Then
			Dim R = ClientRectangle
			'R.Width -= 14
			R.Inflate(-1, -1)
			e.Graphics.FillRectangle(SystemColors.Highlight.Opacity(128).Brush, R)
		End If
	End Sub

	Overridable Sub SelectItem()
		If [ReadOnly] Then Return
		OnItemClicked()
		DroppedDown = False
		RaiseChangeEvent()
	End Sub

	Overridable Sub OnItemClicked()
		If Results.SelectedRows.Count = 0 Then Return
		Text = Results.SelectedRows(0).Cells(1).Value
		m_Value = Results.SelectedRows(0).Cells(0).Value
	End Sub

	Private Sub Results_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Results.MouseMove
		Dim Info As DataGridView.HitTestInfo = Results.HitTest(e.X, e.Y)
		If Info.RowIndex >= 0 Then
			Results.Rows(Info.RowIndex).Selected = True
		End If
	End Sub

	Private Sub Results_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Results.KeyDown
		If e.KeyCode = Keys.Enter Then
			e.Handled = True
			Results.Capture = False
			CloseMenu()
			SelectItem()
			SelectAll()
		End If
	End Sub

	Private Const EC_LEFTMARGIN = &H1
	Private Const EC_RIGHTMARGIN = &H2
	Private Const EM_SETMARGINS = &HD3

	<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
	Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
	End Function


	Public Overrides Sub OnDropDownButtonClicked(e As System.Windows.Forms.MouseEventArgs)
		MyBase.OnDropDownButtonClicked(e)
		If (Now - LastDropDownClosed).Milliseconds > 10 Then DroppedDown = True
	End Sub

	Dim LastDropDownClosed As DateTime

	Sub SortBy(Key As String)
		Results.Sort(Results.Columns(Key), System.ComponentModel.ListSortDirection.Ascending)
	End Sub

	Private Sub DropdownMenu_Closed(sender As Object, e As System.Windows.Forms.ToolStripDropDownClosedEventArgs) Handles DropdownMenu.Closed
		LastDropDownClosed = Now
	End Sub

	Private Sub TmrSelect_Tick(sender As Object, e As System.EventArgs) Handles TmrSelect.Tick
		TmrSelect.Enabled = False
		Buffer = ""
	End Sub

End Class
